package util.hibernate;

import java.io.Serializable;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;

import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.Subqueries;
import org.hibernate.sql.JoinType;

public class DAO<PK extends Serializable> extends HibernateUtil {
	private Session session;
	private Transaction tx;
	// HibernateConfig HibernateFactory;
	private Criteria criteria;
	private DetachedCriteria detachedCriteria;

	public DAO() {
		session = getSessionFactory().openSession();
		tx = session.beginTransaction();
	}

	public void close() {
		tx.commit();
		session.close();
	}

	/*public void notIn() {
		criteria.add(Restrictions.not(expression))
	}*/
	public List<?> query(String hql) {
		Query qry = session.createQuery(hql);
		List<?> list = qry.list();
		return list;
	}
	public void addFilter(String operator, String field, Object value) {
		if (operator.equals("like"))
			criteria.add(Restrictions.like(field, value));
		if (operator.equals("ne"))
			criteria.add(Restrictions.ne(field, value));
		if (operator.equals("eq"))
			criteria.add(Restrictions.eq(field, value));		
	}
	public void addFilter(String operator, String field, PK value) {
		if (operator.equals("like"))
			criteria.add(Restrictions.like(field, value));
		if (operator.equals("ne"))
			criteria.add(Restrictions.ne(field, value));
		if (operator.equals("eq"))
			criteria.add(Restrictions.eq(field, value));		
	}
	public void addFilter(String operator, String field, String value) {
		if (operator.equals("like"))
			criteria.add(Restrictions.like(field, value));
		if (operator.equals("ne"))
			criteria.add(Restrictions.ne(field, value));
		if (operator.equals("eq"))
			criteria.add(Restrictions.eq(field, value));
		if (operator.equals("and"))
			criteria.add(Restrictions.eq(field, value));

	}

	public void addCollectionFilter(String operator, String field, List<?> value) {

		if (operator.equals("in"))
			criteria.add(Restrictions.in(field, value));

	}

	public void searchFilter(String operator, String field, String value) {

		String[] arrayField = field.split(",");

		Disjunction disjunction = Restrictions.disjunction();
		for (int i = 0; i < arrayField.length; i++) {
			// System.out.println(i + " " + arrayField[i]);
			if (operator.equals("like")) {
				Criterion or = Restrictions.like(arrayField[i], "%" + value
						+ "%");
				disjunction.add(or);
			}
			if (operator.equals("ne")) {
				Criterion or = Restrictions.ne(arrayField[i], value);
				disjunction.add(or);
			}
			if (operator.equals("eq")) {
				Criterion or = Restrictions.eq(arrayField[i], value);
				disjunction.add(or);
			}

		}
		criteria.add(disjunction);
		// criteria.add(Restrictions.disjunction().adddisjunction);
	}

	public void addOrder(String sord, String sidx) {
		if (sord.equals("asc")) {
			criteria.addOrder(Order.asc(sidx));
		} else {
			criteria.addOrder(Order.desc(sidx));
		}
	}

	public Long count(Class<?> obj) {
		long count = 0;
		try {
			Criteria crit = session.createCriteria(obj);
			crit.setProjection(Projections.rowCount());
			count = ((Long) crit.list().get(0)).longValue();

		} catch (HibernateException e) {
			handleException(e);
		}
		return count;
	}

	public void createAlias(String from, String to) {
		// criteria.createAlias(from, to);
		criteria.createAlias(from, to);
	}

	public void createDetachedCriteria(Class<?> obj) {
		detachedCriteria = DetachedCriteria.forClass(obj);
	}

	public void createDetachedAlias(String from, String to) {
		detachedCriteria.createAlias(from, to);
	}

	public void createAliasLeftJoin(String from, String to) {
		// criteria.createAlias(from, to);
		criteria.createAlias(from, to, JoinType.LEFT_OUTER_JOIN);
	}

	public Object lastId() {
		Object obj = null;
		try {
			criteria.addOrder(Order.desc("id"));
			criteria.setMaxResults(1);
			obj = criteria.uniqueResult();

		} catch (HibernateException e) {
			handleException(e);
		}
		return obj;
	}

	public void createAliasRightJoin(String from, String to) {
		// criteria.createAlias(from, to);
		criteria.createAlias(from, to, JoinType.RIGHT_OUTER_JOIN);
	}

	public void createCriteria(Class<?> obj) {
		session = getSessionFactory().openSession();
		criteria = session.createCriteria(obj);
		// criteria.setFetchMode(getClass().getName(), FetchMode.JOIN);
	}

	public void groupByLastId(String groupProperty, String field) {
		ProjectionList p = Projections.projectionList();
		p.add(Projections.groupProperty(groupProperty));
		p.add(Projections.max(field));
		detachedCriteria.setProjection(p);
		criteria.add(Subqueries.propertiesIn(new String[] { groupProperty,
				field }, detachedCriteria));
	}

	
	
	protected void addDetachedFilter() {
		criteria.add(Subqueries.propertiesIn(new String[] {
				"strukturJabatan.id", "id" }, detachedCriteria));
	}

	public void delete(Object object) {
		try {
			session.delete(object);
		} catch (HibernateException e) {
			handleException(e);
		}
	}

	protected void deleteAll() {
		session.createSQLQuery("truncate table MyTable").executeUpdate();
	}

	protected void deleteAll(List<?> object) {

		try {
			for (Object obj : object) {
				session.delete(obj);
			}
		} catch (HibernateException e) {
			handleException(e);
		}
	}

	public List<?> find() {
		List<?> obj = null;
		try {
			obj = criteria.list();
		} catch (HibernateException e) {
			handleException(e);
		}
		return (List<?>) obj;
	}

	public Object find(Class<?> obj, PK id) {
		Object retObj = null;
		try {
			retObj = (Object) session.load(obj, id);

		} catch (HibernateException e) {
			handleException(e);
		}
		return retObj;
	}

	public void delete(Class<?> obj, PK id) {

		try {
			session.delete(session.load(obj, id));
		} catch (HibernateException e) {
			handleException(e);
		}
	}

	public List<?> find(int from, int to) {
		List<?> obj = null;
		try {
			criteria.setFirstResult(from).setMaxResults(to);
			obj = criteria.list();
		} catch (HibernateException e) {
			handleException(e);
		}
		return (List<?>) obj;
	}

	public List<?> findAll(Class<?> clazz) {
		List<?> objects = null;
		try {
			Query query = session.createQuery("from " + clazz.getName());
			objects = query.list();
		} catch (HibernateException e) {
			handleException(e);
		}
		return objects;
	}

	protected Object findOne(Class<?> clazz) {
		Object objects = null;
		try {

			Query query = session.createQuery("from " + clazz.getName());
			objects = query.uniqueResult();

		} catch (HibernateException e) {
			handleException(e);
		}
		return objects;
	}

	public Object findUnique() {
		Object obj = null;
		try {
			obj = criteria.uniqueResult();
		} catch (HibernateException e) {
			handleException(e);
		}
		return obj;
	}

	public void flushSession() {
		session.flush();
	}

	protected void handleException(HibernateException e) {
		tx.rollback();
		throw new DataAccessLayerException(e);
	}

	//
	// @SuppressWarnings("unchecked")
	@SuppressWarnings("unchecked")
	public PK insert(Object obj) {

		PK id = null;
		try {
			id = (PK) session.save(obj);

		} catch (HibernateException e) {
			handleException(e);
		}
		return id;
	}

	public void update(Object obj) {
		try {
			session.update(obj);
		} catch (HibernateException e) {
			handleException(e);
		}
	}

	public DetachedCriteria getDetachedCriteria() {
		return detachedCriteria;
	}

	public void setDetachedCriteria(DetachedCriteria detachedCriteria) {
		this.detachedCriteria = detachedCriteria;
	}

}